/**
 ** @author: 浓咖啡
 ** @date:  2018.5.22
 ** @brief: 实现消息的buf缓冲区，类似与socket缓冲区功能
 ** 需要到内核看下socket是如何设计的，这种方案效率太低
 */

#include "msg_buf.h"

#define BUF_MAX 85000  //85KB，和socket接收缓冲区一样大
static int buf_used = 0; //代表缓冲区已经被使用空间大小（字节单位）

static char msg_buf[BUF_MAX];  //定义接收缓冲区
static pthread_mutex_t msg_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
static pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
static bool is_empty = true;
static bool is_full = false;

/**
 * @brief msg_read
 * @param buf 读取数据存储位置
 * @param count 读取字节数
 * @return 0成功 -1表示失败
 */
int msg_read(void *buf, size_t count)
{
    pthread_mutex_lock(&msg_lock);

    //如果缓存空
    if(buf_used == 0){
        //阻塞等待数据
        is_empty = true;
        pthread_cond_wait(&not_empty, &msg_lock);
        is_empty = false;  //跑到这肯定被唤醒，缓冲区有内容
    }

    //如果读取字节数不够，这里我们直接返回错误
    //因为正常情况下肯定是按照协议长度读取数据
    if(count > buf_used){
        pthread_mutex_unlock(&msg_lock);
        return -1;
    }

    //永远是从缓冲区头部开始读取的
    memcpy(buf, msg_buf, count);

    //读完后要重新调整缓冲区
    buf_used -= count;

    //缓冲区越大，算法效率越低，需要重新设计
    memcpy(msg_buf, msg_buf + count, BUF_MAX-count);

    //如果缓冲区满了，那么通知一下等待的线程
    //我们是不允许缓冲满的，所以其实这个条件永远不会成立
    if(is_full){
        pthread_cond_signal(&not_full);
    }
    pthread_mutex_unlock(&msg_lock);
    return 0;
}

/**
 * @brief msg_write
 * @param buf 写入数据缓冲区
 * @param count 要写入的字节数
 * @return 0成功 -1表示失败
 */
int msg_write(const void *buf, size_t count)
{
    pthread_mutex_lock(&msg_lock);

    //epoll中的处理是不允许睡眠的，故这里不能有满等待

    //如果缓冲区空间不够了，直接返回错误
    if(count > (BUF_MAX - buf_used)){
        err_log("msg buf is full, serious err");
        pthread_mutex_unlock(&msg_lock);
        return -1;
    }

    //写入
    memcpy(msg_buf + buf_used, buf, count);

    //读完后要重新调整缓冲区
    buf_used += count;

    //如果缓冲区空，那么通知一下等待线程，这个是经常触发的
    if(is_empty){
        pthread_cond_signal(&not_empty);
    }
    pthread_mutex_unlock(&msg_lock);
    return 0;
}

/**
 * @brief msg_len
 * @return 返回缓冲区已使用长度，调试使用
 */
int msg_len()
{
    return buf_used;
}
